跳到主要内容

Docker 网络配置

外部访问容器

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P-p 参数来指定端口映射。

当使用 -P (大 P)标记时,Docker 会随机映射一个端口到内部容器开放的网络端口。

使用 docker container ls 可以看到,本地主机的 32768 被映射到了容器的 80 端口。此时访问本机的 32768 端口即可访问容器内 NGINX 默认页面。

$ docker run -d -P nginx:alpine

$ docker container ls -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fae320d08268 nginx:alpine "/docker-entrypoint.…" 24 seconds ago Up 20 seconds 0.0.0.0:32768->80/tcp bold_mcnulty

同样的,可以通过 docker logs 命令来查看访问记录。

$ docker logs fa
172.17.0.1 - - [25/Aug/2020:08:34:04 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0" "-"

-p (小 p)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有

ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort

映射所有接口地址

使用 hostPort:containerPort 格式本地的 80 端口映射到容器的 80 端口,可以执行

# 前面是本地端口,后面是容器端口
$ docker run -d -p 80:80 nginx:alpine

映射到指定地址的指定端口

可以使用 ip:hostPort:containerPort 格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1

$ docker run -d -p 127.0.0.1:80:80 nginx:alpine

映射到指定地址的任意端口

使用 ip::containerPort 绑定 localhost 的任意端口到容器的 80 端口,本地主机会自动分配一个端口。

$ docker run -d -p 127.0.0.1::80 nginx:alpine

还可以使用 udp 标记来指定 udp 端口

$ docker run -d -p 127.0.0.1:80:80/udp nginx:alpine

查看映射端口配置

使用 docker port 来查看当前映射的端口配置,也可以查看到绑定的地址

$ docker port fa 80
0.0.0.0:32768

注意:

  • 容器有自己的内部网络和 ip 地址(使用 docker inspect 查看),Docker 还可以有一个可变的网络配置。
  • -p 标记可以多次使用来绑定多个端口

例如:

$ docker run -d \
-p 80:80 \
-p 443:443 \
nginx:alpine

容器互联

如果你之前有 Docker 使用经验,你可能已经习惯了使用 --link 参数来使容器互联。 随着 Docker 网络的完善,强烈建议大家将容器加入自定义的 Docker 网络来连接多个容器,而不是使用 --link 参数。

新建网络

下面先创建一个新的 Docker 网络。

# 创建一个叫做 my-net 的桥接网络
$ docker network create -d bridge my-net

-d 参数指定 Docker 网络类型

连接容器

运行一个容器并连接到新建的 my-net 网络

# 最后一个 sh 是终端类型
$ docker run -it --rm --name busybox1 --network my-net busybox sh

打开新的终端,再运行一个容器并加入到 my-net 网络

$ docker run -it --rm --name busybox2 --network my-net busybox sh

再打开一个新的终端查看容器信息

$ docker container ls

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b47060aca56b busybox "sh" 11 minutes ago Up 11 minutes busybox2
8720575823ec busybox "sh" 16 minutes ago Up 16 minutes busybox1

下面通过 ping 来证明 busybox1 容器和 busybox2 容器建立了互联关系。

busybox1 容器输入以下命令

ping busybox2
/
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms

ping 来测试连接 busybox2 容器,它会解析成 172.19.0.3。

同理在 busybox2 容器执行 ping busybox1,也会成功连接到。

ping busybox1
/ #
PING busybox1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.064 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.143 ms

配置 DNS

如何自定义配置容器的主机名和 DNS 呢?秘诀就是 Docker 利用虚拟文件来挂载容器的 3 个相关配置文件。

在容器中使用 mount 命令可以看到挂载信息:

# mount 它用于挂载Linux系统外的文件。
$ mount
/dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ...
/dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ...
tmpfs on /etc/resolv.conf type tmpfs ...

这种机制可以让宿主主机 DNS 信息发生更新后,所有 Docker 容器的 DNS 配置通过 /etc/resolv.conf 文件立刻得到更新。

配置全部容器的 DNS ,也可以在 /etc/docker/daemon.json 文件中增加以下内容来设置。

{
"dns" : [
"114.114.114.114",
"8.8.8.8"
]
}

这样每次启动的容器 DNS 自动配置为 114.114.114.1148.8.8.8。使用以下命令来证明其已经生效。

$ docker run -it --rm ubuntu:18.04  cat etc/resolv.conf

nameserver 114.114.114.114
nameserver 8.8.8.8

如果用户想要手动指定容器的配置,可以在使用 docker run 命令启动容器时加入如下参数:

  • -h HOSTNAME 或者 --hostname=HOSTNAME 设定容器的主机名,它会被写到容器内的 /etc/hostname/etc/hosts。但它在容器外部看不到,既不会在 docker container ls 中显示,也不会在其他的容器的 /etc/hosts 看到。

  • --dns=IP_ADDRESS 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。

  • --dns-search=DOMAIN 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com

注意:如果在容器启动时没有指定最后两个参数,Docker 会默认用主机上的 /etc/resolv.conf 来配置容器。

Docker 的网络模式

注意,Docker 构建时候的网络模式可以另外设置

docker build –network=host

查看容器的网络类型

docker inspect 容器id | grep -i "network"

Bridge 模式

当 Docker 进程启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。

虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。

在主机上创建一对虚拟网卡 veth pair 设备,Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为 eth0(容器的网卡),另一端放在主机中,以 vethxxx 这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中。

可以通过 brctl show 命令查看。

bridge 模式是 docker 的默认网络模式,不写 –net 参数,就是 bridge 模式。使用 docker run -p 时,docker 实际是在 iptables 做了 DNAT 规则,实现端口转发功能。可以使用 iptables -t nat -vnL 查看。

bridge 模式如下图所示:​​

Host 模式

如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。

容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

Host 模式如下图所示:

演示:

$ docker run -tid --net=host --name docker_host1 ubuntu-base:v3
$ docker run -tid --net=host --name docker_host2 ubuntu-base:v3

$ docker exec -ti docker_host1 /bin/bash
$ docker exec -ti docker_host1 /bin/bash

$ ifconfig –a
$ route –n

Container 模式

这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。

新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。

Container 模式示意图:

演示:

$ docker run -tid --net=container:docker_bri1 \
--name docker_con1 ubuntu-base:v3

$ docker exec -ti docker_con1 /bin/bash
$ docker exec -ti docker_bri1 /bin/bash

$ ifconfig –a
$ route -n

None 模式

使用 none 模式,Docker 容器拥有自己的 Network Namespace,但是,并不为 Docker 容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。

None 模式示意图:

演示:

$ docker run -tid --net=none --name \
docker_non1 ubuntu-base:v3

$ docker exec -ti docker_non1 /bin/bash

$ ifconfig –a
$ route -n

本机的 Docker 网络管理

# 列出Docker的网络
$ docker network ls

NETWORK ID NAME DRIVER SCOPE
e9261a8c9a19 alpine-net bridge local
17e324f45964 bridge bridge local
6ed54d316334 host host local
7092879f2cc8 none null local

Reference ~

参考资料 Docker 的网络模式 参考资料 Docker —— 从入门到实践